package com.jiangli.service.impl;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.Serializers;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.jiangli.constant.RedisConstant;
import com.jiangli.context.BaseContext;
import com.jiangli.dto.MerchantDTO;
import com.jiangli.dto.MerchantPageQueryDTO;
import com.jiangli.dto.MerchantRatingDTO;
import com.jiangli.entity.Dish;
import com.jiangli.entity.Merchant;
import com.jiangli.entity.MerchantRating;
import com.jiangli.entity.UserPreference;
import com.jiangli.exception.BaseException;
import com.jiangli.mapper.DishMapper;
import com.jiangli.mapper.MerchantMapper;
import com.jiangli.mapper.MerchantRatingMapper;
import com.jiangli.mapper.UserPreferenceMapper;
import com.jiangli.result.PageResult;
import com.jiangli.result.Result;
import com.jiangli.service.AdminDishService;
import com.jiangli.service.DishService;
import com.jiangli.service.MerchantService;
import com.jiangli.service.UserPreferenceService;
import com.jiangli.utils.RedisLock;
import com.fasterxml.jackson.core.type.TypeReference;
import com.jiangli.vo.DishVO;
import com.jiangli.vo.MerchantDetailVO;
import com.jiangli.vo.MerchantRaingVO;
import com.jiangli.vo.MerchantVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.jiangli.service.impl.DishServiceImpl.*;

@Service
@Slf4j
public class MerchantServiceImpl implements MerchantService {

//    @Autowired
//    private AdminDishService adminDishService;
    @Autowired
    private DishService dishService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private MerchantMapper merchantMapper;

    @Autowired
    private MerchantRatingMapper  merchantRatingMapper;
    @Autowired
    private DishMapper dishMapper;

    private   Random random=new Random();

    @Autowired
    private ObjectMapper objectMapper;


    // 顶部常量（替换原先类似常量）
    private static final String MERCHANT_BATCH_LOCK_PREFIX = "merchant:batch:lock:";
    private static final String MERCHANT_COUNT = "merchant:total:count";
    private static final String MERCHANT_RECOMMEND = "merchant:recommend";
    private static final String MERCHANT_DETAIL_PREFIX = "merchant:detail:"; // 用作前缀
    private static final String MERCHANT_DISH_IDS = "merchant:dish:ids:";
    private static final String MERCHANT_METADATA_KEY = "merchant:metadata:all";
    private static final String MERCHANT_LOCK_KEY = "merchant:lock:metadata";
    //用与更新商家详细的锁
    private static final String MERCHANT_DETAIL_LOCK = "merchant:detail:lock:";
    private static final String EMPTY_MARKER = "EMPTY";  // 统一空标记，仅此一个
    // TTLs (明确单位)
    private static final long TTL_SHORT_MINUTES = 2L;      // 空值短期缓存（分钟）
    private static final long TTL_MEDIUM_MINUTES = 30L;   // 业务缓存（分钟）
    private static final long TTL_LONG_HOURS = 24L;       // 长缓存（小时）


    private static final String MERCHANT_RATING_USERS = "merchant:rating:users";


    @Autowired
    private RedisLock redisLock;

    @PostConstruct
    public void init(){
        int count=0;
      List<MerchantRating> ratingList=  merchantRatingMapper.getAllRating();
      log.info("mapper 成功");
      log.info(ratingList.toString());
      for(MerchantRating merchantRating:ratingList){
          count++;
          Long merchantId=merchantRating.getMerchantId();
          Long userId = merchantRating.getUserId();
          float rating=merchantRating.getRating();
          String key=MERCHANT_RATING_USERS+merchantId;
          redisTemplate.opsForZSet().add(MERCHANT_RATING_USERS+merchantId,userId.toString(),rating);
          log.info("缓存商家评分:{}",count);
      }
    }



    @Override
    public PageResult getRecommendedMerchantByPage( Integer page, Integer pageSize, boolean reset) {
        
        if(page==null){page=1;}

        if(pageSize==null||pageSize<1||pageSize>100){pageSize=10;}

        //刷新就删除
        if(reset){
            redisTemplate.delete(MERCHANT_RECOMMEND );
        }

       Long merchantCount=getMerchantCount();

        if (merchantCount == 0) {
            return new PageResult(pageSize, Collections.emptyList());
        }
        //获取或 推荐ids 如果未null 生成并且更新
        String key = MERCHANT_RECOMMEND;
        List<Long> recommendIds=getRecommendedMerchantByRedis(key);
        //如果推荐ids不存在 就创建并且保存
        if(recommendIds==null||recommendIds.size()==0||recommendIds.size()!=merchantCount) {
            recommendIds = generateRecommenIds((merchantCount));
            try {
                redisTemplate.opsForValue().set(
                        key, objectMapper.writeValueAsString(recommendIds),
                        TTL_MEDIUM_MINUTES+random.nextInt(10), TimeUnit.SECONDS);
            } catch (Exception e) {
                log.info("保存换缓存失败");
            }
        }

        int offset = (page - 1) * pageSize;
        //计算结束位置
        int end=Math.min(offset + pageSize, recommendIds.size());
        if (offset<end&&end<=recommendIds.size()) {
            List<Long> current = recommendIds.subList(offset, end);
            List<Merchant> currentMerchant=getCurrentMerchant(current);
            return new PageResult(currentMerchant.size(),currentMerchant);
        }
        return new PageResult(0, Collections.emptyList());
    }

    //根据商家id返回商家详细页面
    public MerchantDetailVO showDetail(Long id) {
        MerchantDetailVO merchantDetailVO=new MerchantDetailVO();
        if(id==null){
            throw new BaseException("id为空");
        }
        //尝试从reids中获取基础信息
        Merchant merchant=getMerchantByRedis(id);
        if (merchant == null) {
            throw new BaseException("商家数据库中不存在");
        }
        //复制信息
        BeanUtils.copyProperties(merchant,merchantDetailVO);
        List<Dish> dishList=getMerchantAllDishByMerchantId(id);
        List<DishVO> dishVOList=new ArrayList<>();
        if(dishList==null||dishList.size()==0){
            merchantDetailVO.setDishList(null);
           return merchantDetailVO;
        }
        for (Dish dish : dishList) {
            //只是传入基础信息
            //TODO 传入缺失信息
            DishVO dishVO=new DishVO();
            BeanUtils.copyProperties(dish,dishVO);
            dishVOList.add(dishVO);
        }
        merchantDetailVO.setDishList(dishVOList);
        return merchantDetailVO;
    }

    //对商家评分
    @Transactional
    public void addRate(MerchantRatingDTO merchantRatingDTO) {
       //处理信息缺失问题
        if (merchantRatingDTO.getMerchantId()==null||merchantRatingDTO.getRating()==null){
            throw new BaseException("评分信息不足");
        }
        if (merchantRatingDTO.getRating()>5||merchantRatingDTO.getRating()<0){
            throw new BaseException("评分范围错误");
        }
        MerchantRating merchantRating=new MerchantRating();
        //补充信息
        //需要登录才能获取信息

        BeanUtils.copyProperties(merchantRatingDTO,merchantRating);
        Long userId=BaseContext.getCurrentId();
        if(userId==null){
            throw  new BaseException("用户未登录！");
        }
        merchantRating.setUserId(userId);

        Long merchantId=merchantRatingDTO.getMerchantId();
        /// /从redis中获取用户对商家的评分列表
        Double score = redisTemplate.opsForZSet().score(
                MERCHANT_RATING_USERS + merchantId.toString(),
                userId.toString());
        //  MerchantRating rating = merchantRatingMapper.getRating(merchantRating);
        //如果为null 就直接插入

        merchantRating.setCreateTime(LocalDateTime.now());
        if(score==null){
            updateMerchantRating(merchantRating,false,0);
            try {
                merchantRatingMapper.insert(merchantRating);
            } catch (Exception e) {
                log.error(e.getMessage(),e);
                throw new BaseException("评分失败");
            }
        }else{
            //如果两次评分一致 就不处理
            if(score==Double.valueOf(merchantRating.getRating())){
                return ;
            }
            updateMerchantRating(merchantRating,true,score.floatValue());
            merchantRatingMapper.updateRating(merchantRating);
        }

    }


    //展示商家排名
    public PageResult showMerchantRank( Integer page, Integer pageSize, boolean reset) {
        PageResult recommendedMerchantByPage = getRecommendedMerchantByPage(page, pageSize, reset);
        List<Merchant> records = recommendedMerchantByPage.getRecords();
        // 按销量和评分排序
       List<Merchant> merchantOrder=  records.stream()
                .sorted((d1, d2) -> {
                    // 优先比较销量（降序）
                    if (!(d1.getMerchantRating() ==(d2.getMerchantRating()))) {
                        return d2.getMerchantRating() > d1.getMerchantRating()? 1 : -1;
                    }
                    // 再比较评分（降序）
                    return d2.getRatingCount().compareTo(d1.getRatingCount());
                })
                .collect(Collectors.toList());
       return new PageResult(merchantOrder.size(),merchantOrder);
    }

    @Override
    public MerchantRaingVO getUserMerchantRating(Long userId, Long merchantId) {
        MerchantRaingVO merchantRaing=new MerchantRaingVO();
        merchantRaing.setMerchantId(merchantId);
        merchantRaing.setUserId(userId);
        String key=MERCHANT_RATING_USERS + merchantId.toString();
        Double score = redisTemplate.opsForZSet().score(key, userId.toString());
       if (score==null) {
           return null;
       }
       merchantRaing.setRating(score.floatValue());
       return merchantRaing;
    }


    public void updateMerchantRating(MerchantRating merchantRating, boolean reset, float oldRating) {
        Long merchantId = merchantRating.getMerchantId();
        if (merchantId == null) {
            throw new BaseException("merchantId 为空");
        }
        // 简单校验评分范围（防护）
        Float newSingleRating = merchantRating.getRating();
        if (newSingleRating == null || newSingleRating < 0f || newSingleRating > 5f) {
            throw new BaseException("评分范围错误");
        }

        String lockKey = MERCHANT_DETAIL_LOCK + merchantId;
        redisLock.executeWithLock(lockKey, () -> {
            // 1. 从缓存/DB 获取最新商家信息（复用你的方法）
            List<Long> ids = Collections.singletonList(merchantId);
            List<Merchant> currentMerchant = getCurrentMerchant(ids);
            if (currentMerchant == null || currentMerchant.isEmpty() || currentMerchant.get(0) == null) {
                throw new BaseException("商家信息不存在");
            }
            Merchant merchantCache = currentMerchant.get(0);

            // 安全取当前平均评分与计数（防 null）
            Float maybeCurrentRating = merchantCache.getMerchantRating(); // 可能为 null
            long ratingCount = merchantCache.getRatingCount() == null ? 0L : merchantCache.getRatingCount();
            double currentRatingValue = (maybeCurrentRating == null) ? 0.0d : maybeCurrentRating.doubleValue();

            double computedRating;
            if (!reset) {
                // 新增评分：总分增加，人数+1
                double totalScore = currentRatingValue * (double) ratingCount + (double) newSingleRating;
                ratingCount = ratingCount + 1;
                computedRating = totalScore / (double) ratingCount;
            } else {
                // 修改评分：人数不变（如果 ratingCount == 0，容错处理为首次评分）
                if (ratingCount == 0L) {
                    // 没有历史计数，视为首次评分
                    ratingCount = 1L;
                    computedRating = (double) newSingleRating;
                    log.error("评分错误 修改评分时候总人数不可能为0");
                } else {
                    // 用总分 - old + new 再除以 count
                    double totalScore = currentRatingValue * (double) ratingCount - (double) oldRating + (double) newSingleRating;
                    computedRating = totalScore / (double) ratingCount;
                }
            }

            // 四舍五入/保留合适的小数位（可选），这里保留 float 精度
            float finalRating = (float) computedRating;
            if(finalRating<0||finalRating>5){
                log.error("总评分为负数！{},{}",merchantRating,LocalDateTime.now());
                throw new BaseException("更新商家评分为负数！");
            }
            // 更新内存对象（cache object）
            merchantCache.setMerchantRating(finalRating);
            merchantCache.setRatingCount(ratingCount);
            // 2. 更新数据库（只更新需要的字段）
            //更新redis中的缓存
           ;
            try {
                Merchant merchantDB = new Merchant();
                merchantDB.setId(merchantCache.getId());
                merchantDB.setMerchantRating(merchantCache.getMerchantRating());
                merchantDB.setRatingCount(merchantCache.getRatingCount());
                log.warn("更新商家：{}",merchantCache);
                merchantMapper.update(merchantDB);

            } catch (Exception e) {
                log.error("数据库更新评分失败", e);
                throw new BaseException("数据库更新异常");
            }

            String key2=MERCHANT_RATING_USERS + merchantId;
            try{

                redisTemplate.opsForZSet().add(key2,
                        merchantRating.getUserId().toString(),
                        merchantRating.getRating());
            }catch (Exception e){
                e.printStackTrace();
                log.error("更新用户缓存的评分失败！{}",e.getMessage());
                //更新缓存失败就直接删除错误的数据
                redisTemplate.opsForZSet().remove(key2,
                        merchantRating.getMerchantId().toString());
            }

            // 3. 更新缓存（覆盖写），保证缓存与 DB 一致
            String key = MERCHANT_DETAIL_PREFIX + merchantCache.getId();
            try {
                String json = objectMapper.writeValueAsString(merchantCache);
                redisTemplate.opsForValue().set(key, json, TTL_MEDIUM_MINUTES, TimeUnit.SECONDS);
            } catch (JsonProcessingException e) {
                log.error("商家更新评分存入redis失败", e);
                // 不抛异常，业务（DB）已完成
            }

            return null;
        });
    }



    //通过商家id 从redis/DB 获取商家全部菜品
    private List<Dish> getMerchantAllDishByMerchantId(Long merchantId) {
        List<Long> dishIds=getMerchantAllDishId(merchantId);
        List<Dish> dishesFromCacheOrDB = dishService.getDishesFromCacheOrDB(dishIds);
        return dishesFromCacheOrDB;
    }

    //获取redis/DB获取 商家菜品id 列表
    private List<Long> getMerchantAllDishId(Long merchantId)  {
        // 1. 拼接Key
        String dishIdsKey = MERCHANT_DISH_IDS + merchantId; // 如 "merchant:dish:ids:50"



// 2. 从Redis获取菜品ID数组的JSON字符串
        String dishIdsJson = redisTemplate.opsForValue().get(dishIdsKey);
        List<Long> dishIds;
        //取出为标志 直接返回null
        if(dishIdsJson==EMPTY_MARKER){
            return null;
        }

        if (dishIdsJson == null) {
            // 缓存未命中：查数据库获取该商家的菜品ID列表，再存入Redis
             dishIds= dishMapper.selectDishIdsByMerchantId(merchantId);
             //如果数据库不存在 存入null
             if(dishIds==null||dishIds.size()==0){
                 redisTemplate.opsForValue().set(dishIdsKey,
                         EMPTY_MARKER,
                         TTL_SHORT_MINUTES, TimeUnit.MINUTES);
             }else {
                 try {
                     redisTemplate.opsForValue().set(
                             dishIdsKey,
                             objectMapper.writeValueAsString(dishIds), // 转为JSON数组
                             TTL_MEDIUM_MINUTES+random.nextInt(10),
                             TimeUnit.SECONDS
                     );
                 } catch (Exception e) {
                     log.error("转为json字符串存入redis失败");
                 }
             }

        } else {
            // 缓存命中：反序列化为List<Long>
            try {
                List<Long> dishIdsDB = objectMapper.readValue(dishIdsJson, new TypeReference<List<Long>>() {});
                return dishIdsDB;
            } catch (JsonProcessingException e) {
                log.error("反序列化失败");
                return null;
            }
        }
        return dishIds;
    }


    private Merchant getMerchantByRedis(Long id) {
        String key = MERCHANT_DETAIL_PREFIX + id;
        String cached = redisTemplate.opsForValue().get(key);

        // 1) 统一空标记判断
        if (EMPTY_MARKER.equals(cached)) {
            return null;
        }
        if (cached != null) {
            try {
                return objectMapper.readValue(cached, Merchant.class);
            } catch (Exception e) {
                log.error("反序列化商家缓存失败，删除该缓存并继续查库 key={}", key, e);
                redisTemplate.delete(key);
            }
        }

        // 2) 加分布式锁保护查库和写缓存（避免缓存穿透/击穿）
        Merchant merchant = redisLock.executeWithLock(MERCHANT_DETAIL_LOCK + id, () -> {
            // 双重检查
            String cached2 = redisTemplate.opsForValue().get(key);
            if (EMPTY_MARKER.equals(cached2)) {
                return null;
            }
            if (cached2 != null) {
                try {
                    return objectMapper.readValue(cached2, Merchant.class);
                } catch (JsonProcessingException e) {
                    redisTemplate.delete(key);
                }
            }

            // 真正查库
            Merchant merchantDB = merchantMapper.getByid(id);
            if (merchantDB != null) {
                try {
                    String json = objectMapper.writeValueAsString(merchantDB);
                    // 写中等过期时间（单位分钟，使用你类里已有的 CACHE_EXPIRE_HOURS 值）
                    redisTemplate.opsForValue().set(key, json, TTL_MEDIUM_MINUTES, TimeUnit.SECONDS);
                } catch (JsonProcessingException e) {
                    log.error("序列化失败，跳过写入缓存 key={}", key, e);
                }
                return merchantDB;
            }

            // 数据库也不存在 —— 写短期空值以防穿透
            try {
                redisTemplate.opsForValue().set(key, EMPTY_MARKER, TTL_SHORT_MINUTES, TimeUnit.MINUTES);
            } catch (Exception ex) {
                log.error("写入空值标记失败 key={}", key, ex);
            }
            return null;
        });

        return merchant;
    }


    public List<Merchant> getCurrentMerchant(List<Long> current) {
        List<Merchant> currentMerchant=new ArrayList<>();
        List<Long> lostIds=new ArrayList<>();

        Map<Long, Merchant> MerchantMap = new HashMap<>(current.size());
        for (Long id : current) {
            String key = MERCHANT_DETAIL_PREFIX + id;
            String merchantStr =  redisTemplate.opsForValue().get(key);
            if (merchantStr != null) {
                try {
                    Merchant merchant = objectMapper.readValue(merchantStr,Merchant.class);
                    currentMerchant.add(merchant);
                    MerchantMap.put(id, merchant);
                } catch (Exception e) {
                   log.error("反序列化失败");
                   redisTemplate.delete(key);
                   //添加丢失id
                   lostIds.add(id);
                }
            }else {
                //缓存不存在 加入丢失id中
                lostIds.add(id);
            }
        }

// 2. 查数据库（缓存未命中）
        if (!lostIds.isEmpty()) {
            int shardCount = 4;
            // 按ID哈希分片，避免顺序分片的不均匀问题
            Map<Integer, List<Long>> shardMap = new HashMap<>();
            for (Long id : lostIds) {
                int shardIndex = (int) (id % shardCount);
                shardMap.computeIfAbsent(shardIndex, k -> new ArrayList<>()).add(id);
            }

            for (List<Long> shard : shardMap.values()) {
                if (shard.isEmpty()) continue;
                String lockKey = MERCHANT_BATCH_LOCK_PREFIX + (shard.get(0) % shardCount);
                // 加锁查询，设置锁超时时间30秒
                redisLock.executeWithLock(lockKey,  () -> {
                    // 双重检查缓存
                    List<Long> currentMiss = new ArrayList<>();
                    for (Long id : shard) {
                        String cacheKey = MERCHANT_DETAIL_PREFIX + id;
                        String json = (String) redisTemplate.opsForValue().get(cacheKey);
                        if (json == null) {
                            currentMiss.add(id);
                        } else if (!"NULL".equals(json)) {
                            // 其他线程已缓存，直接反序列化
                            try {
                                MerchantMap.put(id, objectMapper.readValue(json, Merchant.class));
                            } catch (JsonProcessingException e) {
                                log.error("双重检查时反序列化失败", e);
                                redisTemplate.delete(cacheKey);
                                currentMiss.add(id);
                            }
                        }
                    }

                    // 查数据库并更新缓存
                    if (!currentMiss.isEmpty()) {
                        List<Merchant> merchants = merchantMapper.selectByIds(currentMiss);
                        Set<Long> existIds = merchants.stream()
                                .map(Merchant::getId)
                                .collect(Collectors.toSet());

                        // 缓存有效数据
                        for (Merchant merchant : merchants) {
                            try {
                                String cacheKey = MERCHANT_DETAIL_PREFIX + merchant.getId();
                                redisTemplate.opsForValue().set(
                                        cacheKey,
                                        objectMapper.writeValueAsString(merchant),
                                        1,
                                        TimeUnit.HOURS
                                );
                                MerchantMap.put(merchant.getId(), merchant);
                            } catch (JsonProcessingException e) {
                                log.error("缓存写入失败", e);
                            }
                        }

                        // 缓存空值
                        for (Long id : currentMiss) {
                            if (!existIds.contains(id)) {
                                String cacheKey = MERCHANT_DETAIL_PREFIX + id;
                                redisTemplate.opsForValue().
                                        set(cacheKey, "NULL",
                                                TTL_LONG_HOURS, TimeUnit.MINUTES);
                                MerchantMap.put(id, null); // 标记为不存在
                            }
                        }
                    }
                    return null;
                });
            }
        }
        List<Merchant> result = new ArrayList<>();
        for (Long id : current) {
            Merchant m = MerchantMap.get(id);
            if (m != null) {
                result.add(m);
            }
        }
        return result;
    }


    //创建用户 推荐商家
    private List<Long> generateRecommenIds( Long count) {

        //从缓存中获取全部商家信息
        List<Merchant> allmerchants=getAllMerchantsWithCache();

        if (allmerchants == null || allmerchants.size() == 0) {
            return Collections.emptyList();
        }

        //根据商家的评分推推荐
        List<Merchant> merchantOrderByRating = allmerchants.stream()
                .sorted((m1, m2) -> {
                            double w1 = m1.getMerchantRating();
                            double w2 = m2.getMerchantRating();
                            return Double.compare(w2, w1);
                        }
                )
                .collect(Collectors.toList());


        //3. 加入随机商家（例如：20%的概率替换为随机商家）
        List<Merchant> finalRecommend = new ArrayList<>();
        Set<Long> addedIds = new HashSet<>(); // 记录已添加的商家ID，避免重复

        double randomRatio = 0.5; // 20%的概率插入随机商家

        for (Merchant merchant : merchantOrderByRating) {
            // 避免重复添加
            if (addedIds.contains(merchant.getId())) {
                continue;
            }

            // 按概率插入随机商家
            if (random.nextDouble() < randomRatio) {
                // 从所有商家中找一个未添加过的随机商家
                Merchant randomMerchant = getRandomMerchant(allmerchants, addedIds, merchant.getId());
                if (randomMerchant != null) {
                    finalRecommend.add(randomMerchant);
                    //TODO 问题应该就是在这里 当我们随机加入商家的时候这个商家被抛弃了
                   if(!randomMerchant.getId().equals(merchant.getId())) {
                       finalRecommend.add(merchant);
                       addedIds.add(merchant.getId());
                   }
                    addedIds.add(randomMerchant.getId());

                    continue; // 跳过当前商家，继续下一个
                }
            }//
            // 否则添加当前高分商家
            finalRecommend.add(merchant);
            addedIds.add(merchant.getId());


        }
        List<Long> collect = finalRecommend.stream()
                .map(Merchant::getId)
                .collect(Collectors.toList());

        if(collect!=null&&collect.size()!=count) {
            generateRecommenIds(count);
        }
        return collect;
    }

    public List<Merchant> getAllMerchantsWithCache() {
        // 1. 先从缓存获取
        String json =  redisTemplate.opsForValue().get(MERCHANT_METADATA_KEY);

        // 2. 缓存命中空标记，直接返回空列表
        if (json != null && json.equals(EMPTY_MARKER)) {
            return Collections.emptyList();
        }

        // 3. 缓存命中有效数据，反序列化后返回
        if (json != null) {
            try {
                return objectMapper.readValue(json, new TypeReference<List<Merchant>>() {
                });
            } catch (JsonProcessingException e) {
                // 反序列化失败，删除错误缓存
                redisTemplate.delete(MERCHANT_METADATA_KEY);
            }
        }

        // 4. 缓存未命中，加锁查询数据库（防止缓存击穿）
        List<Merchant> allMerchants = redisLock.executeWithLock(MERCHANT_LOCK_KEY, () -> {
            // 5. 双重检查缓存（防止等待锁期间缓存已被其他线程更新）
            String jsonAgain =  redisTemplate.opsForValue().get(MERCHANT_METADATA_KEY);
            if (jsonAgain != null) {
                if (jsonAgain.equals(EMPTY_MARKER)) {
                    return Collections.emptyList();
                }
                try {
                    return objectMapper.readValue(jsonAgain, new com.fasterxml.jackson.core.type.TypeReference<List<Merchant>>() {});
                } catch (JsonProcessingException e) {
                    redisTemplate.delete(MERCHANT_METADATA_KEY);
                }
            }

            // 6. 真正查询数据库
            List<Merchant> merchantsFromDb = merchantMapper.selectALLMerchantMetadata();

            // 7. 处理数据库查询结果
            if (merchantsFromDb == null || merchantsFromDb.isEmpty()) {
                // 数据库结果为空，缓存空标记
                redisTemplate.opsForValue().set(
                        MERCHANT_METADATA_KEY,
                        EMPTY_MARKER,
                       TTL_SHORT_MINUTES,
                        TimeUnit.MINUTES
                );
                return Collections.emptyList();
            }

            // 8. 数据库结果不为空，序列化后存入缓存
            try {
                redisTemplate.opsForValue().set(
                        MERCHANT_METADATA_KEY,
                        objectMapper.writeValueAsString(merchantsFromDb),
                        TTL_MEDIUM_MINUTES+random.nextInt(10),
                        TimeUnit.SECONDS
                );
            } catch (JsonProcessingException e) {
                // 缓存失败不影响主流程，但删除可能的错误缓存
                redisTemplate.delete(MERCHANT_METADATA_KEY);
            }

            return merchantsFromDb;
        });

        // 9. 返回结果（防止空指针）
        return allMerchants != null ? allMerchants : Collections.emptyList();
    }

    /**
     * 从商家列表中随机获取一个未添加过的商家（排除当前商家）
     */
    private Merchant getRandomMerchant(List<Merchant> allMerchants, Set<Long> addedIds, Long currentMerchantId) {
        // 筛选：未添加过，且不是当前商家
        List<Merchant> candidates = allMerchants.stream()
                .filter(m -> !addedIds.contains(m.getId()) && !m.getId().equals(currentMerchantId))
                .collect(Collectors.toList());

        // 如果没有可选商家，返回null
        if (candidates.isEmpty()) {
            return null;
        }

        // 随机返回一个
        return candidates.get(random.nextInt(candidates.size()));
    }




    //获取redis中的推荐id
    private List<Long> getRecommendedMerchantByRedis(String key) {
        String merchants= redisTemplate.opsForValue().get(key);
        if(merchants==null){
            return null;
        }
        try {
            //将redis中的数据提出
            Long[] longs = objectMapper.readValue(merchants, Long[].class);
            //将其转为list返回
            return Arrays.asList(longs);
        } catch (JsonProcessingException e) {
            redisTemplate.delete(key);
            return null;
        }

    }


    private Long getMerchantCount() {
        String count = redisTemplate.opsForValue().get(MERCHANT_COUNT);
        if (count != null) {
           String  countStr = count.toString();
            return Long.parseLong(countStr);
        }
        Long total = redisLock.executeWithLock(MERCHANT_COUNT, () ->
                {
                    Long totalDP = merchantMapper.getAllCount();
                    redisTemplate.opsForValue().set(MERCHANT_COUNT,
                            totalDP.toString(), TTL_MEDIUM_MINUTES, TimeUnit.SECONDS);
                    return totalDP;
                }
        );
        return total;
    }
}
